home *** CD-ROM | disk | FTP | other *** search
- //
- // cnvwrap.m
- // Program for automatically generating degenerate (convenience)
- // methods for methods with optional parameters
- //
- // Written by Carl Lindberg
- // Copyright (c) 1994,1995 by Carl Lindberg.
- // Version 1.2 All rights reserved.
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
- //
-
- // This program takes method declarations - with default values attached
- // to optional parameters - and outputs all possible degenerate methods
- // along with their header lines.
- //
- //
- // Command-line options:
- //
- // -i filename Specify input file. Reads from stdin if this
- // option not used.
- // -m filename Output method implementations to named file. Adds
- // '.m' to filename if not there already. Outputs to
- // stdout if this option is not used.
- // -h filename Output method headers to named file. Adds '.h' to
- // filename if not there already. Outputs to stdout if
- // this option is not used.
- // -f filename Just like -h filename.h -m filename.m. Automatically
- // adds .h and .m extensions to filename, and outputs
- // headers and methods, respectively, to the two files.
- // -e {h|m|hm} Excludes the {header|implementation|both} of the full
- // method from being output. For example, -em will print
- // out all headers, but will not print the implementation
- // of the full method (normally, it is printed with a
- // blank body).
- // -a Appends to output files instead of overwriting.
- // -H Help
- //
- //
- //
- // SYNTAX FOR INPUT LINE(S):
- //
- // Input lines are basically the same as header lines, except that
- // default values to parameters are put inside square brackets and
- // tacked directly onto the end of that parameters. Also, no
- // semicolon at the end.
- //
- //
- // 1) Leave at least one space/tab after the "-" or "+".
- // 2) ONE line per method - don't break up a long method into 2 or more
- // lines.
- // 3) Default values signify an optional parameter. These should be
- // between square brackets tacked on the end of a parameter -
- // NO SPACES before the open bracket.
- // The 'default value' may contain any character - including whitespaces -
- // EXCEPT a right bracket character. Even then, you are allowed
- // to have right bracket character(s) so long as no whitespaces come
- // _after_ it. Ex:
- //
- // Legal: with:(char *)[[sender stringValue]]
- // with:(char *)[[[sender selectedCell]stringValue]]
- // Illegal: with:(char *)[[sender stringValue] ]
- // with:(char *)[[[sender selectedCell] stringValue]]
- //
- // This was as flexible as I could figure out - it's a limitation
- // of the regular expression used to search. If this still isn't
- // enough, than you could simply put in a word like FOOBAR as the
- // default value, and then search-and-replace FOOBAR with the real
- // default value on the output.
- //
- // 4) Every variable must be typecast - even if it's an id, please put
- // (id) as the typecast. A method return type is optional, though.
- // 5) If the first parameter - really the method name - is optional,
- // then *every* parameter must be optional too, or else you will
- // get duplicate methods output.
- //
- //
- // The program works by first parsing each line, and splitting it
- // up into four lists, each list containing some needed portion of
- // each parameter. Then the program goes into a second main loop
- // which works up the needed text using the values in the lists.
- //
- // For the input:
- //
- // - (int)spotOfStr:(const char *)str occurrenceNum:(int)n[0] caseSensitive:(BOOL)sense[YES]
- //
- // the four lists would end up so:
- //
- //
- // paramtypeList paramList paramvalList defaultList
- // (whole param minus any (param name (variable (default
- // default value) only) name) name)
- // -------------------------------- -------------- ------------ -----------
- // Param 1: (int)spotOfStr:(const char *)str spotOfStr: str [blank]
- // Param 2: occurrenceNum:(int)n occurrenceNum: n 0
- // Param 3: caseSensitive:(BOOL)sense caseSensitive: sense YES
- //
- //
- // There should be (number of optional parameters)**2 possible method
- // combinations. The exception is that if the first "parameter"
- // (spotOfStr in the example above) is optional, only one more possible
- // method is added -- the one where every parameter is left out. You
- // can't leave the first parameter out and include others, obviously.
- // Therefore, if the first parameter is optional, *every* parameter should
- // be optional -- else duplicate methods will be output.
- //
- //
- //
- //
- // You may thank the piles of convenience methods in MiscString for
- // prompting me to write this program. :-) Enjoy.
- //
- // Version 1.1 June 7, 1995
- // - fixed bug where having '(' or '[' in the default value section would
- // confuse program
- // - added -e {h|m|hm} switch to exclude outputting of the last possible
- // header or method. This lets you code the full method first, then
- // just output the headers/code for the convenience methods only.
- // Version 1.2 July 8, 1995
- // - changed the regular expression search to allow more possibilities of
- // default section values.
-
- #include <appkit/appkit.h>
- #include <misckit/MiscString.h>
-
- void dohelp();
- void usage();
- void freestuff();
-
- // These are needed to be declared outside main() for freestuff()
- id mainstr, headstring, specstring, hName, mName, iName, returnstr, mfilestr,
- hfilestr, defaultList, paramList, paramtypeList, paramvalList;
-
- void main(int argc, char **argv)
- {
- char ch;
-
- BOOL isOptArray[50]; //ith slot YES if param #i is optional
- BOOL append = NO; //append to given filenames, or overwrite?
- BOOL outputFinalHeader = YES;
- BOOL outputFinalMethod = YES;
- int i, j, numposs, numparam, thenum, len, spot2, spot3, startposs;
-
- FILE *infile, *moutfile, *houtfile;
-
- id tmp;
-
- // Initialize some stuff
- mainstr = [MiscString new];
- specstring= [MiscString new]; //special; used if first param is optional
- headstring= [MiscString new]; //for building up the header line
- hfilestr = [MiscString new]; //for storing method headers
- mfilestr = [MiscString new]; //for storing method definitions
- returnstr = [MiscString new]; //intermediate string, used for making
- //the return value
-
- iName = [MiscString new]; //strings to hold filenames
- mName = [MiscString new];
- hName = [MiscString new];
-
- //for "caseSensitive:(BOOL)sense[YES]":
- defaultList = [List new]; //for storing the default values (eg "YES")
- paramList = [List new]; //for storing the parameter name (eg "caseSensitive:")
- paramvalList = [List new]; //for storing the variable name (eg "sense")
- paramtypeList = [List new]; //for storing whole parameter w/ typecasts
- // (eg "caseSensitive:(BOOL)sense")
-
-
- // These are defaults
- infile = stdin;
- houtfile = stdout;
- moutfile = stdout;
- append = NO;
-
- re_set_syntax(RE_NO_BK_PARENS);
-
- // Process arguments
- while ((ch = getopt(argc, argv, "Ham:h:f:i:e:")) != EOF)
- switch((char)ch) {
- case '?':
- usage(argv[0]);
- case 'H':
- dohelp(argv[0]);
- freestuff();
- exit(0);
- case 'a':
- append = YES;
- break;
- case 'i':
- [iName setStringValue:optarg];
- break;
- case 'm':
- [mName setStringValue:optarg];
- [mName addExtensionIfNeeded:"m"];
- break;
- case 'h':
- [hName setStringValue:optarg];
- [hName addExtensionIfNeeded:"h"];
- break;
- case 'f':
- [hName setStringValue:optarg];
- [hName cat:".h"];
- [mName setStringValue:optarg];
- [mName cat:".m"];
- break;
- case 'e':
- if (index(optarg,'h')) outputFinalHeader = NO;
- if (index(optarg,'m')) outputFinalMethod = NO;
- break;
- default:
- usage(argv[0]);
- exit(2);
- }
-
-
- if ([iName length]) {
- infile = fopen([iName stringValue], "r+");
- if (!infile) {
- fprintf(stderr,"ERROR: could not open '%s'\n",[iName stringValue]);
- freestuff();
- exit(1);
- }
- }
-
- if ([hName length]) {
- houtfile = fopen([hName stringValue], (append)? "a+":"w+");
- if (!houtfile) {
- fprintf(stderr,"ERROR: could not open '%s'\n",[hName stringValue]);
- freestuff();
- exit(1);
- }
- }
-
- if ([mName length]) {
- moutfile = fopen([mName stringValue], (append)? "a+":"w+");
- if (!moutfile) {
- fprintf(stderr,"ERROR: could not open '%s'\n",[mName stringValue]);
- freestuff();
- exit(1);
- }
- }
-
- // Repeat for every method read in... the || [mainstr length] is there
- // if the last line of input does not have a newline at the end -
- // this loop only terminates if at EOF *and* no string was read in.
-
- while ([mainstr fgets:infile keepNewline:NO] != EOF || [mainstr length]) {
- if ([mainstr length] < 5) continue; //break out if a blank line
- numparam = 0;
- numposs = 1;
- startposs = 0;
-
- // Isolate each parameter and process it - the regular expression is
- // what expects to see a cast for each variable.
-
- for (i=0;(spot2 = [mainstr spotOfRegex:"[^\t\n :]+[:][(][^)]*[)][^ \t\n[]+([[][^]]*[]][^ \t\n]*)?"
- occurrenceNum:i length:&len]) >= 0;i++)
- {
- // The above parsing may not have worked if the method return type had
- // spaces in it, such as (const char *). This checks to see if that
- // happened and sets 'spot2' and 'len' to the corrected values.
-
- if ((i == 0) && ((spot3 = [mainstr spotOfRegex:"[(][^)]*[)]"]) < spot2)) {
- len += (spot2-spot3);
- spot2 = spot3;
- }
-
- // Now get the whole parameter in a string.
- tmp = [mainstr midFrom:spot2 length:len];
-
- // Add the parameter name (sans type and variable name) to its list.
- // If the method has a return type, this must be stripped out too -
- // thus the check on the first character.
-
- if ([tmp charAt:0] == '(') {
- [paramList addObject:[tmp midFrom:[tmp spotOf:')']+1 to:[tmp spotOf:':']]];
- }
- else {
- [paramList addObject:[tmp midFrom:0 to:[tmp spotOf:':']]];
- }
-
- numparam++;
- if (![tmp endcmp:"]"]) { // It is an optional param
- id tmp2 = [tmp midFrom:[tmp spotOf:':'] to:[tmp length]];
- int spot = [tmp spotOf:':'] + [tmp2 spotOf:'[']; // Find beginning of default value
- isOptArray[i] = YES;
-
- // Get the whole parameter minus default value and add it to list
- [paramtypeList addObject:[tmp midFrom:0 to:spot-1]];
-
- // Get the default value and add it to its list
- [defaultList addObject:[tmp midFrom:spot+1 to:[tmp length]-2]];
-
- // Get the variable name and add it to its list
- //[paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:spot-1]];
- [paramvalList addObject:[tmp2 midFrom:[tmp2 spotOf:')']+1 to:[tmp2 spotOf:'[']-1]];
- [tmp2 free];
-
- // If the first parameter is optional, it is handled much differently.
- // Instead of doubling the number of potential methods, only one more
- // is added. This is handled by starting the second main loop at -1
- // instead of 0 (startposs). Also, since in the rest of the methods
- // the first parameter must be there, its slot in the isOptArray should
- // be NO, as it really is not optional. 'specstring' holds the header
- // for the extra method incurred -- which is just the name (and return
- // type if there) of the parameter minus the colon. 'specstring' will
- // be used only in the -1th iteration of the second main loop.
-
- if (i==0) {
- [specstring setStringValue:[[tmp left:[tmp spotOf:':']] stringValueAndFree]];
- [specstring insert:" "];
- startposs = -1;
- isOptArray[0] = NO;
- }
- else { // Not the first param, so double # of possible methods.
- numposs *= 2;
- }
-
- // Free tmp here... under the else below, it is added to one of the lists
- // and is therefore freed then.
- [tmp free];
- }
- else { // Not an optional parameter, doesn't have [XXX] at end
- isOptArray[i] = NO;
- [defaultList addObject:[MiscString new]]; // Add a dummy string to keep place
- [paramtypeList addObject:tmp];
- [paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:[tmp length]-1]];
- }
-
-
- } //done parsing the input line
-
- /* //debugging with parsing...
- for (i=0;k<numparam;k++) {
- printf("Full param: %s\n",[[paramtypeList objectAt:i] stringValue]);
- printf("Optional? %s\n",(isOptArray[i])? "YES":"NO");
- printf("Default: '%s'\n",[[defaultList objectAt:i] stringValue]);
- printf("Value: %s\n",[[paramvalList objectAt:i] stringValue]);
- printf("Name: %s\n",[[paramList objectAt:i] stringValue]);
- }
- */
- // Okay, now output stuff. There should be numposs methods to do.
- // 'startposs' is -1 if the first param is optional and the one
- // extra method is there to do, 0 normally.
-
- for (i=startposs;i<numposs;i++) {
- [headstring setStringValue:[[mainstr wordNum:0] stringValueAndFree]]; //"-" or "+"
- [returnstr setStringValue:"{ return [self"];
- thenum = 1;
-
- // Build up the header and return for each method. hfilestr holds
- // the method headers, mfilestr holds the method name, and returnstr
- // holds the return value which gets added to mfilestr at the end of
- // the inner loop.
-
- for (j=0;j<numparam;j++) {
-
- // Add the method name to the return value. The argument will
- // either be the variable name or the default
- [returnstr addChar:' '];
- [returnstr concatenate:[paramList objectAt:j]];
-
- // Check to see if the current parameter deserves to be added
- // in this pass. If it does, then add it to the header,
- // and add the variable name to returnstr. Otherwise, just add
- // the default value to returnstr.
- // The special case of i==-1 should not do this part - that is handled
- // elsewhere. If the current parameter is not optional then it should
- // always be added in.
- // Otherwise, the third condition determines whether a parameter is
- // included in this pass. 'thenum' starts as 1 every iteration of i,
- // meaning the value of that condition for the first parameter will
- // be 0, then 1, then 0, then 1... as i goes up. 'thenum' gets doubled
- // each optional parameter, so for the second optional parameter the
- // condition will be 0, 0, 1, 1, 0, 0, 1, 1... as i goes up. The third
- // will be 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, ... and so on. Since i represents
- // the total number of combinations, this will account for every possible
- // combination.
-
- if ((i>=0) && (!(isOptArray[j]) || ((i % (thenum*2)) >= thenum))) {
- [headstring addChar:' '];
- [headstring concatenate:[paramtypeList objectAt:j]];
- [returnstr concatenate:[paramvalList objectAt:j]]; //the variable name
- }
- else {
- [returnstr concatenate:[defaultList objectAt:j]]; //the default value
- }
- if (isOptArray[j]) thenum *= 2;
- }
-
- // Special case handling for i==-1. Only the else portion of the above
- // will be executed, so only the returnstr is done. specstring, set in
- // the parsing loop, holds the header needed by hfilestr and mfilestr.
- // So, just add it to them.
-
- if (i==-1) {
- [headstring concatenate:specstring];
- }
-
- [returnstr cat:"]; }"];
-
- if ((i <(numposs-1)) || outputFinalHeader) {
- [hfilestr concatenate:headstring];
- [hfilestr cat:";\n"];
- }
- if ((i < (numposs-1)) || outputFinalMethod) {
- [mfilestr concatenate:headstring];
- }
- // If we are at the last possible method (i == numposs-1) we don't want
- // to put a method body in - it would just call itself and be an infinite
- // loop. I just decided to put in a blank method body - you will at least
- // get a warning from the compiler if you forget to take this out or implement
- // it. At least if you use -Wall.
- // Otherwise, returnstr holds the entire method body, so add that to mfilestr.
-
- if (i < (numposs-1)) //don't put in return for richest method
- [mfilestr catFromFormat:"\n%s\n\n",[returnstr stringValue]];
- else if (outputFinalMethod)
- [mfilestr cat:"\n{\n}\n\n"];
- }
-
- [hfilestr addChar:'\n']; // Leave a blank line between sets of headers
- [mfilestr cat:"\n\n\n"]; // Leave three blank lines between sets of
- // convenience methods.
-
- // Empty out all the lists to start over with the next input line
- [defaultList freeObjects];
- [paramList freeObjects];
- [paramvalList freeObjects];
- [paramtypeList freeObjects];
- }
-
- // Output the info... yeah, it only gets output at the end instead of
- // as it's processed, but that's the price to pay if you want headers
- // grouped together instead of interspersed with the actual methods
- // when both outputs go to stdout.
-
- fprintf(houtfile,"%s\n\n",[hfilestr stringValue]);
- fprintf(moutfile,"%s\n",[mfilestr stringValue]);
-
- // And clean up...
-
- if ([hName length]) fclose(houtfile);
- if ([mName length]) fclose(moutfile);
- if ([iName length]) fclose(infile);
- freestuff();
-
- }
-
- void freestuff()
- {
- [hName free];
- [mName free];
- [iName free];
- [specstring free];
- [headstring free];
- [returnstr free];
- [mfilestr free];
- [hfilestr free];
- [[defaultList freeObjects] free];
- [[paramList freeObjects] free];
- [[paramvalList freeObjects] free];
- [[paramtypeList freeObjects] free];
- [mainstr free];
- }
-
- void usage(char *progname)
- {
- fprintf(stderr,"\nusage: %s [-Ha] [-i inputfilename]\n",progname);
- fprintf(stderr," [-h hfilename -m mfilename | -f basefilename]\n");
- fprintf(stderr," [-e {h|m|hm}]\n\n");
- fprintf(stderr," %s -H for more verbose help.\n\n",progname);
- freestuff();
- exit(2);
- }
-
-
- void dohelp(char *progname)
- {
- printf("usage: %s [-Ha] [-i filename] [-e {h|m|hm}]\n",progname);
- printf(" [-h hfilename -m mfilename | -f basefilename]\n\n");
- printf(" This program takes method headers with specified default values for\n");
- printf(" optional parameters, and outputs both headers and methods for\n");
- printf(" all possible degenerate (convenience) methods.\n");
- printf(" Reads from stdin if infile not specified, and outputs headers\n");
- printf(" and/or methods to stdout if destination is not specified.\n\n");
- printf(" -H this help\n");
- printf(" -a append to named output files; don't overwrite\n");
- printf(" -i take input from named file\n");
- printf(" -h/-m output headers/methods to named file. Adds '.h'/'.m' to\n");
- printf(" filename if not there already.\n");
- printf(" -f same as -h basefilename.h -m basefilename.m\n");
- printf(" -e {h|m|hm} Excludes final {header|method|both} from being output.\n");
- printf("\n");
- printf(" INPUT SYNTAX: (one line per method)\n");
- printf(" - (int)spotOf:(char)aChar caseSensitive:(BOOL)sense[YES]\n");
- printf(" ^ ^ ^__________________________^ ^\n");
- printf(" | | | |__NO space\n");
- printf(" SPACE | CAST each parameter--even if id\n");
- printf(" Method cast is optional\n");
-
- }
-